package znet

import (
	"errors"
	"io"
	"log"
	"net"
	"sync"
	"zinxp/utils"
	"zinxp/ziface"
)

type Connection struct {
	//当前Conn属于哪个Server
	TcpServer ziface.IServer
	//当前连接的socket TCP套接字
	Conn *net.TCPConn
	//当前连接的ID 也可以称作为SessionID，ID全局唯一
	ConnID uint32
	//当前连接的关闭状态
	isClosed bool
	//消息管理MsgId和对应处理方法的消息管理模块
	MsgHandler ziface.ImsgHandle
	//告知该链接已经退出/停止的channel
	ExitBuffChan chan bool
	//无缓冲通道，用于读、写两个goroutine之间的消息通信
	msgChan chan []byte
	//有缓冲通道，用于读、写两个goroutine之间的消息通信
	msgBuffChan chan []byte

	//连接属性
	property map[string]interface{}
	//保护连接属性修改的锁
	propertyLock sync.RWMutex
}

//新创建连接
func NewConnection(server ziface.IServer, conn *net.TCPConn, connID uint32, msgHandler ziface.ImsgHandle) *Connection {
	c := &Connection{
		TcpServer:    server,
		Conn:         conn,
		ConnID:       connID,
		isClosed:     false,
		MsgHandler:   msgHandler,
		ExitBuffChan: make(chan bool, 1),
		msgChan:      make(chan []byte),
		msgBuffChan:  make(chan []byte, utils.GlobalObject.MaxWorkerTaskLen),
		property:     make(map[string]interface{}),
	}
	//将新创建的Conn添加到链接管理中
	c.TcpServer.GetConnMgr().Add(c)
	return c
}

//处理Conn读数据的goroutine
func (c *Connection) StartReader() {
	log.Println("[Reader goroutine is running ...]")
	defer log.Println(c.RemoteAddr().String(), " conn reader exit!")
	defer c.Stop()

	for {
		//创建拆包解包的对象
		dp := NewDataPack()

		headData := make([]byte, dp.GetHeadLen())
		if _, err := io.ReadFull(c.GetTCPConnection(), headData); err != nil {
			log.Println("read msg head error ", err)
			c.ExitBuffChan <- true
			return
		}
		//拆包，得到msgid 和 datalen 放在msg中
		msg, err := dp.Unpack(headData)
		if err != nil {
			log.Println("unpack error ", err)
			c.ExitBuffChan <- true
			return
		}
		//根据datalen读取data
		var data []byte
		if msg.GetDataLen() > 0 {
			data = make([]byte, msg.GetDataLen())
			if _, err := io.ReadFull(c.GetTCPConnection(), data); err != nil {
				log.Println("read msg data error ", err)
				c.ExitBuffChan <- true
				return
			}
		}
		msg.SetData(data)
		//调用当前链接业务(这里执行的是当前conn的绑定的handle方法)
		//获取当前客户端请求的Request数据
		req := Request{
			conn: c,
			msg:  msg,
		}
		//从绑定好的消息和对应的处理方法中执行对应的Handle方法
		if utils.GlobalObject.WorkerPoolSize > 0 {
			c.MsgHandler.SendMsgToTaskQueue(&req)
		} else {
			go c.MsgHandler.DoMsgHandler(&req)
		}
	}
}

//启动连接
func (c *Connection) Start() {
	//1 开启用户从客户端读取数据流程的Goroutine
	go c.StartReader()
	//2 开启用于写回客户端数据流程的Goroutine
	go c.StartWriter()

	//按照用户传递进来的创建连接时需要处理的业务，执行钩子方法
	c.TcpServer.CallOnConnectStart(c)
}

//关闭连接
func (c *Connection) Stop() {
	if c.isClosed == true {
		return
	}
	c.isClosed = true
	//TODO Connection Stop() 如果用户注册了该链接的关闭回调业务，那么在此刻应该显式调用
	c.TcpServer.CallOnConnectStop(c)

	c.Conn.Close()
	//通知从缓冲队列读数据的业务，该链接已经关闭
	c.ExitBuffChan <- true
	//将链接从连接管理器中删除
	c.TcpServer.GetConnMgr().Remove(c)
	close(c.ExitBuffChan)
	close(c.msgBuffChan)
}

func (c *Connection) GetTCPConnection() *net.TCPConn {
	return c.Conn
}

func (c *Connection) GetConnID() uint32 {
	return c.ConnID
}

func (c *Connection) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

func (c *Connection) SendMsg(msgid uint32, data []byte) error {
	if c.isClosed == true {
		return errors.New("Connection closed when send message")
	}

	//data 封包发送
	dp := NewDataPack()
	msg, err := dp.Pack(NewMsgPackage(msgid, data))
	if err != nil {
		log.Println("Pack error msg id = ", msgid)
		return errors.New("Pack error msg")
	}

	//发送给客户端,Reader讲发送客户端的数据改为发送至Channel
	c.msgChan <- msg
	//if _, err := c.Conn.Write(msg); err != nil {
	//	log.Println("Write msg id ", msgid, " error ", err)
	//	c.ExitBuffChan <- true
	//	return errors.New("conn Write error")
	//}
	return nil
}

func (c *Connection) SendBufMsg(msgID uint32, data []byte) error {
	if c.isClosed == true {
		return errors.New("Connection closed when send buff msg")
	}

	dp := NewDataPack()
	msg, err := dp.Pack(NewMsgPackage(msgID, data))
	if err != nil {
		log.Println("Pack error msgID= ", msgID)
		return errors.New("Pack Message error")
	}
	c.msgBuffChan <- msg
	return nil
}

func (c *Connection) SetProperty(key string, value interface{}) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()
	c.property[key] = value
}

func (c *Connection) GetProperty(key string) (interface{}, error) {
	c.propertyLock.RLock()
	defer c.propertyLock.RUnlock()
	if value, ok := c.property[key]; ok {
		return value, nil
	} else {
		return nil, errors.New("no property found")
	}
}

func (c *Connection) RemoveProperty(key string) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()
	delete(c.property, key)
}

func (c *Connection) StartWriter() {
	log.Println("[Writer goroutine is running]")
	defer log.Println(c.RemoteAddr().String(), "[conn Writer exit!]")
	for {
		select {
		case data := <-c.msgChan:
			if _, err := c.Conn.Write(data); err != nil {
				log.Println("Send Data error ", err, " Conn Writer exit!")
				return
			}
		case data, ok := <-c.msgBuffChan:
			if ok {
				if _, err := c.Conn.Write(data); err != nil {
					log.Println("Send Data error ", err, " Conn Writer exit!")
					return
				}
			} else {
				break
				log.Println("MsgBufChan is Closed")
			}
		case <-c.ExitBuffChan:
			return
		}
	}
}
